# -*- coding: utf-8 -*-
#  Clase para presentar código Python coloreado e indentado en html. 

## vipera, un diseñador de aplicaciones para Python
##    Copyright (C) 2011 Ángel Luis García García
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import tokenize, keyword, os, sys, codecs
import StringIO as cStringIO

from i18n_vipera import t

class py_to_html(object):
    '''Parsear código Python para pasarlo a formato HTML'''
    # Colores.
    colores = {
        'keywords':'blue',
        'comentarios':'red',
        'cadenas':'green'
    }
    
    def __init__(self, fichero_py, fichero_html, idioma = None):
        '''Constructor de clase'''
        if fichero_html.rfind('.html') == -1: fichero_html += '.html'
        # Idioma.
        self.l = idioma
        # Atributos.
        self.__fich_py = fichero_py
        self.__fich_html = fichero_html
        self.__codigo = None

    def __codigo_html(self, opcion, nombre = u'vipera: código Python'):
        '''Método de cabecera y fin de código HTML.'''
        texto = ''
        if opcion == 'cabecera':
            texto = u'''
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <title>%s</title>
      <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
      <body>
        <h3> vipera: Código Python </h3>
        <h4>
    ''' %  t(nombre, self.l)
    
        if opcion == 'pie':
            texto = u'''
        </h4>
      </body>
    </html>
    '''
        # Devolvemos el texto html.
        return texto
    
    def __tags_html(self, texto, tokens, color):
        '''Método de inserción de tags html.'''
        offset = 0
        # Tags de html. 
        tag_color_ini = '<span style="color: %s;">' % self.colores[color]
        tag_color_fin = '</span>'
        len_tag = len(tag_color_ini) + len(tag_color_fin)
        linea_anterior = None
        # Buscamos comentarios.
        for j in tokens:
            # Coordenadas de texto a colorear.
            (l, inicio), (_, fin) = j
            if linea_anterior is None: linea_anterior = l
            elif linea_anterior <> l:
                offset = 0
                linea_anterior = l
            aux = list(texto[l - 1])
            aux.insert(inicio + offset, tag_color_ini) 
            if fin == -1 and color == 'cadenas': aux.append(tag_color_fin) 
            else: aux.insert(fin + offset + 1, tag_color_fin) 
            # Incrementamos el desplazamiento offset.
            texto[l - 1] = ''
            for k in aux: texto[l - 1] += k
            offset += len_tag

    def cargar_fichero(self, nombre_fichero = None):
        '''Método de carga de fichero.'''
        if nombre_fichero is None:
            nombre_fichero = self.__fich_py
        else:
            self.__fich_py = nombre_fichero
        fichero = codecs.open(os.path.realpath(nombre_fichero),'r','utf-8')
        f = fichero.read()
        fichero.close()
        f = f.replace('>','&gt;')
        f = f.replace('<','&lt;')
        # Código.
        self.__codigo = f
    
    def __tokenizar_codigo(self, f):  
        '''Método para insertar código html en código python.'''
        try:
            # Iteramos sobre cada línea de f.
            texto = cStringIO.StringIO(f)
            tokens = tokenize.generate_tokens(texto.readline)
        
            # Obtenemos posición de las cadenas dentro del texto.
            cadenas = []
        
            for a,b,c,d,e in tokens:
                if a == tokenize.STRING:
                    if c[0] <> d[0]: 
                        cadenas.append((c,(c[0],-1)))
                        for j in range(c[0] + 1,d[0]):
                            cadenas.append(((j,0),(j,-1)))
                        cadenas.append(((d[0],0),d))
                    else: cadenas.append((c,d))
        
            # Convertimos la cadena en una lista.
            k = f.split("\n")
            texto = []
            for i in k: texto.extend([i])
        
            # Insertamos código html.
            self.__tags_html(texto, cadenas,'cadenas')
            f = ''
            for i in texto: f += '%s%s' % (i,'\n')
        
            # Iteramos sobre cada línea de f.
            texto = cStringIO.StringIO(f)
            tokens = tokenize.generate_tokens(texto.readline)
        
            # Obtenemos posición de los comentarios dentro del texto.
            comentarios = []
        
            for a,b,c,d,e in tokens:
                if a == tokenize.COMMENT:
                    comentarios.append((c,d))
        
            # Convertimos la cadena en una lista.
            k = f.split("\n")
            texto = []
            for i in k: texto.extend([i])
        
            # Insertamos código html.
            self.__tags_html(texto, comentarios,'comentarios')
            f = ''
            for i in texto: f += '%s%s' % (i,'\n')
        
            # Iteramos sobre cada línea de f.
            texto = cStringIO.StringIO(f)
            tokens = tokenize.generate_tokens(texto.readline)
        
            # Obtenemos posición de las palabras clave dentro del texto.
            keywords = []
            for a,b,c,d,e in tokens:
                if a == tokenize.NAME:
                    if b in keyword.kwlist: keywords.append((c,d))
        
            # Convertimos la cadena en una lista, para insertar </br>.
            k = f.split("\n")
            texto = []
            for i in k: texto.extend([i])
        
            # Insertamos código html.
            self.__tags_html(texto, keywords,'keywords')
            f = ''
            for i in texto: f += '%s%s' % (i,'\n')
        
            # Convertimos cadena en lista...
            k = f.split("\n")
            texto = []
            for i in k:
                # Reemplazamos los primeros blancos a la izquierda.
                blancos_a_izq = len(i) - len(i.lstrip())
                i = i.replace(' ','&nbsp;', blancos_a_izq)
                i = ('%s%s') % (i,' </br>')
                texto.extend([i])
        
            # Insertamos en el texto la cabecera y pie del documento html.
            texto.insert(0,self.__codigo_html('cabecera'))
            texto.append(self.__codigo_html('pie'))  
            f = ''
            for i in texto: f += '%s%s' % (i,'\n')
        
            # Liberamos memoria.
            tokens.close()
        
            # Devolvemos el código parseado.
            return f, ""
        except IndentationError as (message):
            msj = t(u"Error de indentación de código", self.l) + \
                u"\n\n%s" % message
            return None, msj
        except:
            msj = t(u"Error al analizar el código Python", self.l)
            return None, msj
        
    def crear_fichero_html(self):
        '''Creación de fichero html.'''
        # Creamos un fichero.
        nombre_fichero = self.__fich_html
        f, msj = self.__tokenizar_codigo(self.__codigo)
        # Si hay errores se devuelve nulo.
        if f is not None:
            f_html = codecs.open(os.path.realpath(nombre_fichero), 'w', 'utf-8')
            f_html.write(f)
            f_html.close()
            # Devolvemos el estado correcto.
            return True, ""
        # Devolvemos el estado incorrecto y el mensaje de error.
        return False, msj
